/**
 * Copyright (c) 2012, Hakans Pirates - 
 * Johan Andersson spaiki17@gmail.com, 
 * Mattias Lundberg lundberg.mattias@gmail.com, 
 * Samuel Sjodin sjoodin@gmail.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *  
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package pirates.ship;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.asset.ModelKey;
import com.jme3.audio.AudioRenderer;
import com.jme3.bullet.BulletAppState;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.input.FlyByCamera;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.network.Client;
import com.jme3.network.Server;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.BillboardControl;
import com.jme3.texture.Texture;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import pirates.Pirates;
import pirates.PiratesServer;
import pirates.Settings;
import pirates.util.CollisionShapeFactory;
import static pirates.Settings.*;
import pirates.notifications.Notify;
import pirates.util.CollisionShapeFactory;

/**
 * 
 * @author Johan
 */
public class ShipState extends AbstractAppState {

    private SimpleApplication app;
    private Node rootNode;
    private Node shipNode = new Node(Settings.SHIP_NODE);
    private AssetManager assetManager;
    private AppStateManager stateManager;
    private InputManager inputManager;
    private ViewPort viewPort;
    private BulletAppState bullet;
    private FlyByCamera flyCam;
    private Camera cam;
    private AudioRenderer audioRenderer;
    private KeyInput keyInput;
    private Client client;// null if run on server
    private Server server;// null if run on client
    private Node ship;
    private BitmapText nameText;

    public ShipState(Pirates pirates, Client client) {
        init(pirates);
        this.client = client;
    }

    public ShipState(PiratesServer piratesServer, Server server) {
        init(piratesServer);
        this.server = server;
    }

    void init(SimpleApplication app) {
        this.app = app;
        this.rootNode = this.app.getRootNode();
        this.assetManager = this.app.getAssetManager();
        this.stateManager = this.app.getStateManager();
        this.inputManager = this.app.getInputManager();
        this.viewPort = this.app.getViewPort();
        this.bullet = this.stateManager.getState(BulletAppState.class);
        this.flyCam = this.app.getFlyByCamera();
        this.cam = this.app.getCamera();
        this.audioRenderer = this.app.getAudioRenderer();
        rootNode.attachChild(shipNode);
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);

    }

    public void createShip(EntityModels shipModel, Vector3f location,
            String name, Map<String, Object>... values) {
        Map<String, Object> temp = new HashMap<String, Object>();
        for (Map<String, Object> map : values) {
            temp.putAll(map);
        }
        Notify.debug(shipModel.getValues().get(ShipConstants.MODEL_PATH).toString());
        _createShip(
                new ModelKey(shipModel.getValues().get(ShipConstants.MODEL_PATH).toString()), location,
                name, shipModel.getValues(), temp);
    }

    public void createShip(String shipModel, Vector3f location, String name,
            Map<String, Object>... values) {
        _createShip(new ModelKey(shipModel), location, name, values);
    }

    public void createShip(ModelKey shipModel, Vector3f location, String name,
            Map<String, Object>... values) {
        _createShip(shipModel, location, name, values);
    }

    private void _createShip(ModelKey shipModelKey, Vector3f location,
            String name, Map<String, Object>... values) {
        Spatial shipModel = assetManager.loadModel(shipModelKey);

        Material mat_ship = new Material(assetManager,
                "Common/MatDefs/Misc/Unshaded.j3md");
        Texture texture = assetManager.loadTexture("Models/Ships/battle.png");
        mat_ship.setTexture("ColorMap", texture);
        shipModel.setMaterial(mat_ship);

        Node cameraNode = new Node(Settings.CAMERA_NODE);
        cameraNode.setLocalTranslation(-600, 200, 0);
        cameraNode.lookAt(shipModel.getWorldTranslation(), Vector3f.UNIT_Y);

        ship = new Node(name);
        ship.attachChild(shipModel);
        ship.attachChild(cameraNode);

        if (values != null) {
            for (Map<String, Object> map : values) {
                loadValues(ship, map);
            }
        }

        if (!name.equals(Settings.HOST_NAME)) {
            // Display player name over the ship if not this player.
            BitmapFont guiFont = app.getAssetManager().loadFont(
                    "Interface/Fonts/Default.fnt");
            nameText = new BitmapText(guiFont);
            nameText.setSize(guiFont.getCharSet().getRenderedSize()); // font
            // size
            String team = ship.<String>getUserData(ShipConstants.TEAM + "");
            nameText.setColor(ColorRGBA.Red); // font color
            // if(team.equals(EntityTeam.BRITISH+"")){
            // nameText.setColor(ColorRGBA.Blue);
            // }
            // else if(team.equals(EntityTeam.NEUTRAL+"")){
            // nameText.setColor(ColorRGBA.Green);
            // }
            nameText.setLocalTranslation(-10, 200, -25); // position

            BillboardControl control = new BillboardControl();
            nameText.setText(name);
            nameText.addControl(control);
            ship.attachChild(nameText);
        }

        ShipControl shipControl = new ShipControl(
                CollisionShapeFactory.createBox(ship), 1f, app,
                bullet.getPhysicsSpace(), this);

        ship.setLocalTranslation(location);
        shipNode.attachChild(ship);

        ship.addControl(shipControl);

        bullet.getPhysicsSpace().add(ship);
        if (Settings.DEBUG) {
            bullet.getPhysicsSpace().enableDebug(assetManager);
        }
        bullet.getPhysicsSpace().addCollisionListener(shipControl);

    }

    private Geometry findGeom(Spatial spatial, String name) {
        if (spatial instanceof Node) {
            Node node = (Node) spatial;
            for (int i = 0; i < node.getQuantity(); i++) {
                Spatial child = node.getChild(i);
                Geometry result = findGeom(child, name);
                if (result != null) {
                    return result;
                }
            }
        } else if (spatial instanceof Geometry) {
            if (spatial.getName().startsWith(name)) {
                return (Geometry) spatial;
            }
        }
        return null;
    }

    public Node getShip(String name) {
        return (Node) shipNode.getChild(name);
    }

    public boolean removeShip(String name) {
        return true ? shipNode.detachChildNamed(name) > 0 : false;
    }

    private void loadValues(Spatial spatial, Map<String, Object> values) {
        for (Entry<String, Object> value : values.entrySet()) {
            spatial.setUserData(value.getKey(), value.getValue());
        }
    }

    /**
     * 
     * PS. This list will eventually get outdated.
     * 
     * @returns a new linked list containing the pirates of the game.
     */
    private LinkedList<Spatial> getPirates() {
        LinkedList<Spatial> pirates = new LinkedList<Spatial>();

        Iterator<Spatial> i = shipNode.getChildren().iterator();
        while (i.hasNext()) {
            Spatial ship = i.next();
            if (ship.<String>getUserData(Settings.ShipConstants.TEAM+"").
                    equals(Settings.EntityTeam.PIRATES.toString())) {
                pirates.add(ship);
            }
        }
        return pirates;
    }

    /**
     * 
     * PS. This list will eventually get outdated.
     * 
     * @returns a new linked list containing the pirates of the game.
     */
    private LinkedList<Spatial> getBrittish() {
        LinkedList<Spatial> brittish = new LinkedList<Spatial>();

        Iterator<Spatial> i = shipNode.getChildren().iterator();
        while (i.hasNext()) {
            Spatial ship = i.next();
            if (ship.<String>getUserData(Settings.ShipConstants.TEAM.toString()).
                    equals(Settings.EntityTeam.BRITISH.toString())) {
                brittish.add(ship);
            }
        }
        return brittish;
    }

    /**
     * @return the current number of pirate human players
     */
    public int getNrOfPirates() {
        return getPirates().size();
    }

    /**
     * @return the current number of brittish human players
     */
    public int getNrOfBrittish() {
        return getBrittish().size();
    }

    public boolean weNeedMorePirates() {
        int nrOfPirates = getNrOfPirates();
        int nrOfBrittish = getNrOfBrittish();
        Notify.debug("Nr of pirates: " + nrOfPirates);
        Notify.debug("Nr of brittish: " + nrOfBrittish);
        Notify.debug("Player should be pirate? "
                + (nrOfBrittish > 0 && (nrOfPirates / nrOfBrittish) <= Settings.TEAM_DISTRIBUTION_PIRATES));
        if (nrOfBrittish > 0) {
            float share = nrOfPirates / nrOfBrittish;
            return (share <= Settings.TEAM_DISTRIBUTION_PIRATES);
        } else {
            return false; // we want the first player to join the brittish team.
        }
    }

    public Iterator<Spatial> getAIships() {
        LinkedList<Spatial> aiShips = new LinkedList<Spatial>();
        Iterator<Spatial> i = shipNode.getChildren().iterator();
        while (i.hasNext()) {
            Spatial ship = i.next();
            try {
                if (ship.<String>getUserData(Settings.ShipConstants.OWNER.toString()).
                        equals(Settings.HOST_NAME)) {
                    aiShips.add(ship);
                }
            } catch (Exception e) {
                //
            }

        }

        return aiShips.iterator();
    }

    public Node getShipsNode() {
        return ship;
    }
}
